理解 ECMAScript 规范 这一合集,第一章主要讲了规范中的一些基础语法,注意,这里是规范的语法,
类似于 ! 和 ?,问号比较常用,它的作用类似于解除包装后的值,这一语法挺像rust 的 Some去匹配Option。
介绍完基本语法,第二章讲了非常实用的一个js操作,就是取值。
obj.foo 这个操作经历了什么,文章中是从底向上描述的。
如果是从顶向底描述,则是,obj.foo 这一个字符串先被词法分析器解析,解析之后,获得一个Reference对象,这个对象中包含了基值 obj 和键值 foo,之后就会传给GetValue
GetValue
是一个规范内部的方法,接受一个参数V,这个参数的类型是一个Reference,Reference中就包含了基值和键值,读这个方法,可以了解js的一些基本执行原理,下面是我自己对这个方法的描述;
如果V不是Reference类型,就直接返回V,
获取Reference中的Base,即基值,然后判断基值是否为原始类型,就是number,string这些,如果是的话,就得把这些类型转为包装类型再调用其方法。如果不是原始类型,就调用`[[Get]]`方法,这个方法需要传入V的键值名字,还有一个Receiver,
Receiver是通过GetThisValue(V)获取的,这里的GetThisValue,其实相当于获取V的Base,基值。
[[Get]]
本身也是一个方法,
而[[Get]](P,Receiver)
里面直接调用了?OrdinaryGet(O,P,Receiver),所以,直接看看OrdinaryGet的实现
OrdinaryGet 中详细定义了取值的规范。
下面我就把重要的过程描述一下:
取obj中的foo的值的时候,首先看对象本身有没有这个键值,如果没有,就调用GetPrototypeOf得到对象的原型,然后再调用原型的`[[Get]](P,Receiver)`,这里就会产生递归,就会沿着原型链递归的查找键值。
那如果对象本身有这个键值呢?那就直接返回就好了,这里要判断一下,是能直接取到值,还是被getter函数包装了,如果被getter函数包装了,就再调用一下getter函数,然后返回值。这里规范内部调用getter的时候,还需要传Receiver
上面的过程,可以明显看出,整个调用过程的Receiver就没变过!
还可以注意到,Receiver一直传,一直传,传到了哪里?有什么用?
用一个例子来解释下。
看下面的代码:
const o1 = { x: 10, get foo() { return this.x; } };
const o2 = { x: 50 };
Object.setPrototypeOf(o2, o1);
o2.foo;
// → 50
调用o2.foo的时候,就找到了一个o1的getter函数,这里重要了,因为调用的是getter,所以调用getter方法,
getter方法传入的Receiver就是o2,进入到函数中,再使用this时,这里的this就是指的Receiver o2,因为上面所说,Receiver就没变过。
所以,直接得出 o2.foo的值就是50,而不是o1中的10。
从这个例子可以看出,规范中的一些定义,可以帮我们理清一些js怪异的行为,还是比较有帮助的。
怎么去阅读规范,那就是直接打开网页 https://tc39.es/ecma262/
评论
0